package net.xo68.destiny.starter.worker.logger;


import lombok.extern.slf4j.Slf4j;
import net.xo68.destiny.core.util.ThreadUtils;
import net.xo68.destiny.starter.worker.SchedulerWorkerProperties;
import net.xo68.destiny.starter.worker.logger.impl.JobLoggerImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 任务实例日志工厂
 * @author wuxie
 * @version 2020-10-28
 */
@Slf4j
@Component
public final class JobLoggerFactory implements ApplicationContextAware, SmartInitializingSingleton, DisposableBean {

    private static ApplicationContext applicationContext=null;

    /**
     * 初始化
     */
    public static final int WORKER_STATE_INIT=0;
    /**
     * 已经启动
     */
    public static final int WORKER_STATE_STARTED=1;
    /**
     * 关闭的
     */
    public static final int WORKER_STATE_SHUTDOWN=2;

    /**
     * 日志对象缓存
     */
    private final ConcurrentHashMap<Long, JobLogger> LOGGERS = new ConcurrentHashMap<>();
    /**
     * 日志事件队列
     */
    private final LinkedBlockingQueue<JobLoggingEvent> eventQueue= new LinkedBlockingQueue<>();

    /**
     * 日志对象创建锁
     */
    private final ReentrantLock newLock=new ReentrantLock();
    /**
     * 日志写入线程
     */
    //private final Thread workerThread;

    private final ScheduledExecutorService executorService;

    private final SchedulerWorkerProperties schedulerWorkerProperties;
    private final JobLoggerWriter jobLoggerWriter;

    /**
     * 状态
     */
    private final AtomicInteger workState=new AtomicInteger(WORKER_STATE_INIT);

    public JobLoggerFactory(SchedulerWorkerProperties schedulerWorkerProperties, JobLoggerWriter jobLoggerWriter) {
        this.schedulerWorkerProperties=schedulerWorkerProperties;
        this.jobLoggerWriter=jobLoggerWriter;

        jobLoggerWriter.setEventQueue(eventQueue);
        jobLoggerWriter.setWorkState(this.workState);
        //this.workerThread= ThreadUtils.newThread("jobLogger",jobLoggerWriter);
        this.executorService=ThreadUtils.newSingleThreadScheduled("jobLogger");
    }

    public LinkedBlockingQueue<JobLoggingEvent> eventQueue(){
        return eventQueue;
    }



    public JobLogger getLogger(long loggerIndex){
        newLock.lock();
        if (!LOGGERS.containsKey(loggerIndex)) {
            //创建
            JobLogger logger = new JobLoggerImpl(loggerIndex,eventQueue);
            LOGGERS.put(loggerIndex, logger);
        }
        newLock.unlock();
        return LOGGERS.get(loggerIndex);
    }

    /**
     * 从缓存移除
     * @param loggerIndex
     */
    public void remove(long loggerIndex){
        LOGGERS.remove(loggerIndex);
    }


    @Override
    public void destroy() throws Exception {
        //eventQueue.clear();
        if(this.workState.compareAndSet(WORKER_STATE_STARTED,WORKER_STATE_SHUTDOWN)){
            //this.workerThread.join();
            this.executorService.shutdown();
        }
        LOGGERS.clear();
    }

    @Override
    public void afterSingletonsInstantiated() {
        switch (this.workState.get()){
            case WORKER_STATE_INIT:
                if(this.workState.compareAndSet(WORKER_STATE_INIT,WORKER_STATE_STARTED)){
                    this.executorService.scheduleWithFixedDelay(this.jobLoggerWriter,3000L,schedulerWorkerProperties.getLoggerFlushMilli(), TimeUnit.MILLISECONDS);
                }
                break;
            case WORKER_STATE_STARTED:
            case WORKER_STATE_SHUTDOWN:
                break;
            default:
                throw new Error("Invalid WorkerState");
        }

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        JobLoggerFactory.applicationContext=applicationContext;
    }
}
